﻿//*** Guidance generated code ***//
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Linq.Expressions;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

using ProjectBase.Core;
using ProjectBase.Core.Web;
using ProjectBase.Mvc.Views;

namespace ProjectBase.Mvc
{
    /// <summary>
    ///     There should be placed or ProjectBase.Core common Extensions
    /// </summary>
    public static class ControllerExtensions
    {
        #region As ContentControl
        public static DefinitionListItem AsDefinitionListItem(this IVisualControl control, string labelText = null, bool localizeLabel = true)
        {
            PublishingManager.Publish(control, Severity.Debug, "DefinitionListItem creating");
            return new DefinitionListItem(label: labelText, doLocalize: localizeLabel)
            {
                control
            };
        }
        #endregion As ContentControl

        #region IViewModel
        public static string Controller(this IViewModel model)
        {
            return model.MasterModel.ControllerName;
        }
        public static string Action(this IViewModel model)
        {
            return model.MasterModel.CurrentAction;
        }
        public static bool IsAction(this IViewModel model, string action)
        {
            return model.MasterModel.CurrentAction.IsEqual(action);
        }
        public static bool IsActionFind(this IViewModel model)
        {
            return model.IsAction(Constants.Actions.Find);
        }
        public static bool IsActionDetail(this IViewModel model)
        {
            return model.IsAction(Constants.Actions.Detail);
        }
        public static bool IsAction(this IViewModel model, params string[] actions)
        {
            return actions.Any(action => model.MasterModel.CurrentAction.IsEqual(action));
        }

        #endregion IMasterModel

        #region IWebController<IViewModel> ActionModel
        /// <summary>
        ///     Adds new IActionItem to the list
        /// </summary>
        /// <param name = "actionsList">Not null action list</param>
        /// <param name = "actionName">Action name </param>
        /// <param name = "controllerName">Controller hosting the action</param>
        /// <param name = "actionValue">ID of entity or null</param>
        /// <param name = "href">the URL for anchor</param>
        /// <param name = "target">name of the window for the target</param>
        /// <param name = "index"></param>
        /// <returns></returns>
        public static IList<IActionItem> AddAction(this IList<IActionItem> actionsList, string actionName,
            string controllerName, string actionValue, string href, string target, string text = null,
            int index = -1)
        {
            Contract.Requires(actionsList.Is(), "Null, Cannot add action into null list");
            var action = new ActionItem
            {
                ActionName = actionName,
                ControllerName = controllerName,
                ActionValue = actionValue,
                Text = text ?? actionName,
                Href = href,
                Target = target,
            };

            var old = actionsList
                .Where(a => a.ActionName.IsEqual(actionName))
                .FirstOrDefault();
            if (old.Is())
            {
                actionsList.Remove(old);
            }

            if (index >= 0
                && index < actionsList.Count)
            {
                actionsList.Insert(index, action);
            }
            else
            {
                actionsList.Add(action);
            }
            return actionsList;
        }

        /// <summary>
        ///     Appends new action to Model.MasterModel.ActionsModel.Actions colleciton
        /// </summary>
        /// <param name = "controller"></param>
        /// <param name = "actionName"></param>
        /// <param name = "url"></param>
        /// <param name = "target"></param>
        /// <param name="text"></param>
        /// <returns></returns>
        public static IWebController<IViewModel> AddAction(this IWebController<IViewModel> controller, string actionName,
            IUrlHelper url, string target = null, string text = null)
        {
            return controller.InsertAction(actionName, url, target, text);
        }

        /// <summary>
        ///     Inserts new action into Model.MasterModel.ActionsModel.Actions collection
        /// </summary>
        /// <param name = "controller"></param>
        /// <param name="text"></param>
        /// <param name = "index"></param>
        /// <param name = "actionName"></param>
        /// <param name = "url"></param>
        /// <param name = "target"></param>
        /// <returns></returns>
        public static IWebController<IViewModel> InsertAction(this IWebController<IViewModel> controller,
            string actionName, IUrlHelper url, string target = null, string text = null,
            int index = -1)
        {
            var entityController = controller as IWebController<IEntityModel<IPersistentObject, IFilter>>;
            var id = entityController.ItemID();
            var href = url.Action(actionName, controller.ControllerName);//, new { id });
            controller.Model.MasterModel.ActionsModel.Actions.AddAction(actionName, controller.ControllerName, null, href,
                target, text, index);
            return controller;
        }

        /// <summary>
        ///     Removes every action which ActionName is equal to provided string
        /// </summary>
        /// <param name = "controller">IWebController[IViewModel] instance</param>
        /// <param name = "actionName">filtered action name</param>
        /// <returns></returns>
        public static IWebController<IViewModel> RemoveAction(this IWebController<IViewModel> controller,
            string actionName)
        {
            var actions = controller.Model.MasterModel.ActionsModel.Actions.Where(a => a.ActionName == actionName).ToList();
            if (actions.Is() && actions.Count > 0)
            {
                for (var i = actions.Count; i > 0; )
                {
                    controller.Model.MasterModel.ActionsModel.Actions.Remove(actions[--i]);
                }
            }
            return controller;
        }

        /// <summary>
        ///     Removes every action which ActionName and ControllerName is equal to provided strings
        /// </summary>
        /// <param name = "controller">IWebController[IViewModel] instance</param>
        /// <param name = "actionName">filtered action name</param>
        /// <param name = "controllerName">filtered controller name</param>
        /// <returns></returns>
        public static IWebController<IViewModel> RemoveAction(this IWebController<IViewModel> controller,
            string actionName, string controllerName)
        {
            var actions = controller.Model.MasterModel.ActionsModel.Actions
                .Where(a => a.ActionName == actionName
                            && a.ControllerName == controllerName).ToList();
            if (actions.Is()
                &&
                actions.Count > 0)
            {
                for (var i = actions.Count; i > 0; )
                {
                    controller.Model.MasterModel.ActionsModel.Actions.Remove(actions[--i]);
                }
            }
            return controller;
        }
        #endregion IWebController<IViewModel> ActionModel

        #region IWebController<IViewModel> ButtonModel
        /// <summary>
        ///     Adds new IButtonItem to the list
        /// </summary>
        /// <param name = "buttonsList">Not null button list</param>
        /// <param name = "buttonName">Unique button identifier in the collection</param>
        /// <param name = "formRouteValues">key-pair value collection</param>
        /// <param name = "controllerName"></param>
        /// <param name = "index"></param>
        /// <param name = "text">the text of the button (if differs from the button name)</param>
        /// <returns></returns>
        public static IList<IButtonItem> InsertButton(this IList<IButtonItem> buttonsList, string buttonName,
            RouteValueDictionary formRouteValues, string controllerName,
            int index = 0, string text = null)
        {
            Contract.Requires(buttonsList.Is(), "Null, Cannot add button into null list");
            var button = new ButtonItem(buttonName)
            {
                FormRouteValues = formRouteValues,
                Text = text ?? buttonName,
                FormController = controllerName,
            };

            var old = buttonsList
                .Where(a => a.ButtonName.IsEqual(buttonName))
                .FirstOrDefault();
            if (old.Is())
            {
                buttonsList.Remove(old);
            }

            if (index >= 0
                && index < buttonsList.Count)
            {
                buttonsList.Insert(index, button);
            }
            else
            {
                buttonsList.Add(button);
            }
            return buttonsList;
        }

        /// <summary>
        ///     Appends ButtonItem into Controllers.Model.MasterModel.ButtonsModel.Buttons collection
        /// </summary>
        /// <param name = "controller">controller holding the model</param>
        /// <param name = "buttonName">text which will belocalized and used as action name</param>
        /// <param name = "url">Helper</param>
        /// <param name = "formRouteValues"></param>
        /// <param name = "text">the text of the button (if differs from the button name)</param>
        /// <returns></returns>
        public static IWebController<IViewModel> AddButton(this IWebController<IViewModel> controller, string buttonName,
            IUrlHelper url, RouteValueDictionary formRouteValues = null,
            string text = null)
        {
            var entityController = controller as IWebController<IEntityModel<IPersistentObject, IFilter>>;
            var id = entityController.ItemID();
            if (formRouteValues.IsNull())
            {
                formRouteValues = new RouteValueDictionary();
            }
            if (!formRouteValues.ContainsKey(Constants.Common.RouteValuesId))
            {
                //formRouteValues[Constants.Common.RouteValuesId] = id;
            }
            controller.Model.MasterModel.ButtonsModel.Buttons.InsertButton(buttonName, formRouteValues,
                controller.ControllerName, -1, text);
            return controller;
        }

        public static IWebController<IViewModel> InsertButton(this IWebController<IViewModel> controller, int index,
            string buttonName, IUrlHelper url, RouteValueDictionary formRouteValues = null, string text = null)
        {
            var entityController = controller as IWebController<IEntityModel<IPersistentObject, IFilter>>;
            var id = entityController.ItemID();
            if (formRouteValues.IsNull())
            {
                formRouteValues = new RouteValueDictionary();
            }
            if (!formRouteValues.ContainsKey(Constants.Common.RouteValuesId))
            {
                //formRouteValues[Constants.Common.RouteValuesId] = id;
            }
            controller.Model.MasterModel.ButtonsModel.Buttons.InsertButton(buttonName,
                formRouteValues,
                controller.ControllerName, index, text);
            return controller;
        }

        /// <summary>
        ///     Removes every button which ButtonName is equal to provided string
        /// </summary>
        /// <param name = "controller">IWebController[IViewModel] instance</param>
        /// <param name = "buttonName">filtered button name</param>
        /// <returns></returns>
        public static IWebController<IViewModel> RemoveButton(this IWebController<IViewModel> controller,
            string buttonName)
        {
            var buttons = controller.Model.MasterModel.ButtonsModel.Buttons
                .Where(a => a.FormAction.IsEqual(buttonName))
                .ToList();
            if (buttons.Is()
                &&
                buttons.Count > 0)
            {
                for (var i = buttons.Count; i > 0; )
                {
                    controller.Model.MasterModel.ButtonsModel.Buttons.Remove(buttons[--i]);
                }
            }
            return controller;
        }

        /// <summary>
        ///     Removes button with the same FormAction and FormController
        /// </summary>
        /// <param name = "controller"></param>
        /// <param name = "formAction"></param>
        /// <param name = "formController"></param>
        /// <returns></returns>
        public static IWebController<IViewModel> RemoveButton(this IWebController<IViewModel> controller,
            string formAction, string formController)
        {
            var buttons = controller.Model.MasterModel.ButtonsModel.Buttons
                .Where(a => a.FormAction.IsEqual(formAction)
                            && a.FormController.IsNotEmpty()
                            && a.FormController.IsEqual(formController))
                .ToList();
            if (buttons.Is()
                &&
                buttons.Count > 0)
            {
                for (var i = buttons.Count; i > 0; )
                {
                    controller.Model.MasterModel.ButtonsModel.Buttons.Remove(buttons[--i]);
                }
            }
            return controller;
        }
        #endregion IWebController<IViewModel> ButtonModel

        #region IEntityController
        public static string ItemID(this IWebController<IEntityModel<IPersistentObject, IFilter>> controller)
        {
            if (controller.IsNull() || controller.Model.Item.IsNull())
            {
                return string.Empty;
            }
            return controller.Model.Item.IDToDisplay;
        }
        #endregion IEntityController

        #region Fix MVC bugs
        /// <summary>
        ///     Tries to find out, if the result will be the Page.
        ///     The only (until now) known way is via the Result.ViewName.
        ///     if ViewName is not null nor empty
        ///     and does not contain the ".ascx" - true is returned
        /// </summary>
        /// <param name = "filterContext"></param>
        /// <returns></returns>
        public static bool HasResultMasterPage(this ResultExecutedContext filterContext)
        {
            return HasResultMasterPage(filterContext.Result);
        }

        /// <summary>
        ///     Tries to find out, if the result will be the Page.
        ///     The only to that moment known way is via the Result.ViewName.
        ///     If this ends with ".aspx" true is returned, else false
        /// </summary>
        /// <param name = "filterContext"></param>
        /// <returns></returns>
        public static bool HasResultMasterPage(this ResultExecutingContext filterContext)
        {
            return HasResultMasterPage(filterContext.Result);
        }

        static bool HasResultMasterPage(ActionResult actionResult)
        {
            if (actionResult.Is())
            {
                var emptyResult = actionResult as EmptyResult;
                if (emptyResult.Is())
                {
                    return false;
                }
                var viewResult = actionResult as ViewResult;
                if (viewResult.Is())
                {
                    if (viewResult.View is IMasterPageLessView)
                    {
                        return false;
                    }
                    var name = viewResult.ViewName;
                    if (name.StartsWith(Constants.WebControls.ControlsPath)
                      || name.StartsWith(Constants.WebControls.ControlsPath2))
                    {
                        return false;
                    }
                    if (!name.EndsWith(".ascx", StringComparison.OrdinalIgnoreCase))
                    {
                        return true;
                    }
                }
            }
            return false;
        }
        #endregion Fix MVC bugs

        #region ListView cell
        public static ICellDescription Cell<TSource, TResult>(this TSource entity, Expression<Func<TResult>> expr,
            ICellDescription cell = null)
        {
            return Cell<TSource, TResult, object>(entity, expr, cell);
        }

        public static ICellDescription Cell<TSource, TResult, TSort>(this TSource entity, Expression<Func<TResult>> expr,
            Expression<Func<TSort>> orderBy)
        {
            return Cell(entity, expr, null, orderBy);
        }

        /// <summary>
        ///     Creates the ICellDescription and sets the SortByObject, SortByProperty, IsBoolean, HeaderName, Value
        ///     based on the provided Expression.
        ///     The 'expr' parameter must be ExpressionType.MemberAccess (else exception is in runtime thrown).
        ///     The 'cell' parameter could have preset some properties (e.g. HeaderName). When the Format is set
        ///     the expr result is converted to string using ToDisplay(cell.Format)
        /// </summary>
        /// <typeparam name = "TSource"></typeparam>
        /// <typeparam name = "TResult">any type which will be passed into the ToDisplay() and as a string set into the cell.Value</typeparam>
        /// <typeparam name = "TSort"></typeparam>
        /// <param name = "entity">expecting the IPersistentObject</param>
        /// <param name = "expr">the MemeberAccess expression, which will be parsed and executed</param>
        /// <param name = "cell">cell entity with preset properties (HeaderName, Format)</param>
        /// <param name = "orderBy"></param>
        /// <returns>ICellDescription with set properties</returns>
        /// <exception cref = "NotSupportedException">if the provided expression body is not ExpressionType.MemberAccess</exception>
        public static ICellDescription Cell<TSource, TResult, TSort>(this TSource entity, Expression<Func<TResult>> expr,
            ICellDescription cell = null,
            Expression<Func<TSort>> orderBy = null)
        {
            if (expr.Body.NodeType
                != ExpressionType.MemberAccess)
            {
                throw new NotSupportedException(
                    "Provided 'Expression<Func<TResult>> expr' parameter must be of a 'ExpressionType.MemberAccess'. ");
            }
            cell = cell ?? new CellDescription();
            // I. The Memeber Name represents the SortByProperty string value
            var body = expr.Body as MemberExpression;

            if (cell.HeaderName.IsNull()) // property was not set (in passed cell param)
            {
                cell.HeaderName = body.Member.Name;
            }

            // II. value type align right
            //     boolean will be rendered as the checkbox...
            if (typeof(TResult).IsValueType)
            {
                cell.Align = Align.right;
            }
            if (typeof(TResult).IsEquivalentTo(typeof(bool))
                || typeof(TResult).IsEquivalentTo(typeof(bool?)))
            {
                cell.IsBoolean = true;
                cell.Align = Align.center;
            }

            if (cell.SortProperty.IsNull() // property was not set (in passed cell param)
                || orderBy.Is()) // there is orderby expression
            {
                var orderBody = body;
                if (orderBy.Is())
                {
                    if (orderBy.Body.NodeType
                        != ExpressionType.MemberAccess)
                    {
                        throw new NotSupportedException(
                            "Provided 'Expression<Func<object>> orderBy = null' parameter must be of a 'ExpressionType.MemberAccess'. ");
                    }
                    orderBody = orderBy.Body as MemberExpression;
                }

                // III. let's get the SortByObject from the body
                string path = null;
                // if there is a direct 'entity.Property' the SortByObject will be set to (remain) null

                while (orderBody.Is()) // recursive SortByObject builder
                {
                    // 'entity.City.Address.Name' ==> 'City.Address'
                    var name = orderBody.Member.Name;
                    orderBody = orderBody.Expression as MemberExpression;
                    if (orderBody.Is())
                    {
                        path = path.Is()
                                   ? name + "." + path
                                   : name;
                    }
                }
                cell.SortProperty = path;
            }

            // IV. finally set the value of this cell
            var result = expr.Compile();
            try // there must be try for some cases, for instance when the Nested property object is null
            {
                // e.g. entity.Parent.Code
                var value = result(); // could be set to null for entity.Parent
                cell.Value = cell.Format.Is() // and the null object reference is therefore called
                                 ? result().ToDisplay(cell.Format)
                                 : result().ToDisplay();
            }
            catch { }

            return cell;
        }
        #endregion ListView cell

        #region Excel sheet cell
        /// <summary>
        ///     Converts the property into the Cell instance used for Excel Exporter.
        ///     The property name is Localized and used as the output header description.
        /// </summary>
        /// <typeparam name = "TSource"></typeparam>
        /// <typeparam name = "TResult"></typeparam>
        /// <param name = "entity"></param>
        /// <param name = "expr"></param>
        /// <param name = "localizedName">if provided is used as the Header name</param>
        /// <returns></returns>
        public static IExCell ExCell<TSource, TResult>(this TSource entity, Expression<Func<TResult>> expr, string localizedName = null)
        {
            if (expr.Body.NodeType
                != ExpressionType.MemberAccess)
            {
                throw new NotSupportedException(
                    "Provided 'Expression<Func<TResult>> expr' parameter must be of a 'ExpressionType.MemberAccess'. ");
            }
            // I. The Memeber Name represents the SortByProperty string value
            var body = expr.Body as MemberExpression;


            // II. If the name is null, then let's get it from the expression
            if (localizedName.IsEmpty())
            {
                localizedName = body.Member.Name.GetLocalized();
            }

            // III. let's get the SortByObject from the body
            var orderBody = body;
            string path = null;
            // if there is a direct 'entity.Property' the SortByObject will be set to (remain) null

            while (orderBody.Is()) // recursive SortByObject builder
            {
                // 'entity.City.Address.Name' ==> 'City.Address'
                var part = orderBody.Member.Name;
                orderBody = orderBody.Expression as MemberExpression;
                if (orderBody.Is())
                {
                    path = path.Is() ? part + "." + path : part;
                }
            }

            // III. Create the resulting cell without value and default type == NUMBER
            var cell = new ExCell
            {
                Name = path,
                Localized = localizedName,
                Type = FindExCellType<TResult>(),
            };

            // IV. finally set the value of this cell
            var result = expr.Compile();
            try // there must be try for some cases, for instance when the Nested property object is null
            {
                // e.g. entity.Parent.Code
                cell.Value = result(); // could be set to null for entity.Parent

                var persistent = result() as IPersistentObject;
                if (persistent.Is())
                {
                    cell.Type = ExCellType.String;
                    cell.Value = persistent.IDToDisplay;
                }
            }
            catch { }

            return cell;
        }

        static ExCellType FindExCellType<TResult>()
        {
            // should be converted to Excel date
            if (typeof(TResult).IsEquivalentTo(typeof(DateTime))
              || typeof(TResult).IsEquivalentTo(typeof(DateTime?)))
            {
                return ExCellType.DateTime;
            }
            // should it be rendered as a string?
            if (typeof(TResult).IsEquivalentTo(typeof(string))
              || typeof(TResult).IsEquivalentTo(typeof(Guid))
              || typeof(TResult).IsEquivalentTo(typeof(Guid?)))
            {
                return ExCellType.Guid;
            }

            // is it bool?
            if (typeof(TResult).IsEquivalentTo(typeof(bool))
              || typeof(TResult).IsEquivalentTo(typeof(bool?)))
            {
                return ExCellType.Boolean;
            }
            return ExCellType.Number;
        }
        #endregion Excel sheet cell

        #region PropertyList
        /// <summary>
        ///     Projection list helps to create the SELECT which returns only listed properties.
        ///     This extension expects the MemberAccess expression (e.g.
        ///     Model.Item.Country.Code,
        ///     Model.Item.User.Login,
        ///     Model.Item.ID)
        ///     which is converted into the string ("Country.Code", "User.Login", "ID").
        ///     For nested objects (e.g. Country.Code) VirtualProperty must be also set (e.g. "Code").
        ///     This property will be then filled with projection result.
        ///     First level properties ("ID", "Name") will use its own properties,
        ///     and virtualProperty parameter can remain null (not provided)
        /// </summary>
        /// <typeparam name = "TFilter"></typeparam>
        /// <typeparam name = "TResult"></typeparam>
        /// <param name = "filter"></param>
        /// <param name = "propertyPath">Member access expression (e.g.
        ///     () => Model.Item.Country.Code,
        ///     () => Model.Item.Contract.User.Login,
        ///     () => Model.Item.ID )</param>
        /// <param name = "virtualProperty">It could be null, when first level property is accessed (Model.Item.ID).
        ///     This is intended to be used for virtual property
        ///     mapping for nested objects. E.g. for virtual property "Code", this value can target
        ///     "Country.Code" (the property Code of property Country). Dao can use it for JOIN Select </param>
        /// <returns></returns>
        public static TFilter AddPropertyToProjectionList<TFilter, TResult>(this TFilter filter,
            Expression<Func<TResult>> propertyPath,
            Expression<Func<TResult>> virtualProperty =
                null)
            where TFilter : class, IFilter
        {
            if (filter.IsNull()
                || propertyPath.IsNull())
            {
                return filter;
            }
            if (propertyPath.Body.NodeType
                != ExpressionType.MemberAccess)
            {
                throw new NotSupportedException(
                    "The 'Expression<Func<TResult>> propertyPath' parameter must be of a 'ExpressionType.MemberAccess'. ");
            }
            if (virtualProperty.Is()
                && virtualProperty.Body.NodeType != ExpressionType.MemberAccess)
            {
                throw new NotSupportedException(
                    "The 'Expression<Func<TResult>> virtualProperty' parameter must be of a 'ExpressionType.MemberAccess'. ");
            }

            // I. like () => Model.Item.Country.Code
            var body = propertyPath.Body as MemberExpression;
            string path = null; // if there is a direct 'entity.Property' the SortByObject will be set to (remain) null

            while (body.Is()) // recursive SortByObject assembling
            {
                // 'entity.City.Address.Name' ==> 'City.Address'
                var name = body.Member.Name;
                body = body.Expression as MemberExpression;
                if (body.Is())
                {
                    if (body.Member.Name.Equals("Model"))
                    {
                        break;
                    }
                    path = path.Is()
                               ? name + "." + path
                               : name;
                }
            }

            var mappedProperty = path;

            if (virtualProperty.Is())
            {
                // it could like be Model.Item.Code
                body = virtualProperty.Body as MemberExpression;
                if (body.Is())
                {
                    mappedProperty = body.Member.Name;
                }
            }

            filter.PropertyList[path] = mappedProperty;

            return filter;
        }
        #endregion PropertyList

        #region Save and Return
        /// <summary>
        /// Every submit button appends its name to FORM. Check if filtered 'actionName'
        /// matches the name of the submit button
        /// </summary>
        /// <param name="request"></param>
        /// <param name="actionName"></param>
        /// <returns></returns>
        public static bool IsSubmitButton(this HttpRequestBase request, string actionName)
        {
            return request.Form.AllKeys.Contains(Constants.Actions.Special.SubmitButtonName)
                && request.Form[Constants.Actions.Special.SubmitButtonName].IsEqual(actionName);
        }
        #endregion Save and Return

        #region Route Values
        public static RouteValueDictionary QueryStringToDictionary(this IWebController<IViewModel> controller,
            string[] exceptKeys = null)
        {
            //var dictionary = new Dictionary<string, object>();
            var dictionary = new RouteValueDictionary();
            var queryString = controller.ControllerContext.HttpContext.Request.QueryString;

            // re append query string
            foreach (var key in queryString.AllKeys)
            {
                if (key.IsNull())
                {
                    continue;
                }
                if (exceptKeys.Is()
                    && exceptKeys.Contains(key))
                {
                    continue;
                }

                var values = queryString.GetValues(key);
                if (values.IsNull()
                    || values.Count() == 0)
                {
                    continue;
                }
                if (values.Count() > 1)
                {
                    var list = new List<string>();
                    foreach (var value in values)
                    {
                        list.Add(value);
                    }
                    dictionary[key] = list;
                    continue;
                }
                dictionary[key] = values.First();
            }
            return dictionary;
        }
        #endregion Route Values

        #region Async Table
        public static JTable SetNoResult(this JTable table, string text)
        {
            table.Caption.Clear();
            table.Head.Clear();
            table.Rows.Clear();
            table.NoResult.Add(new JRowCell { Text = text });
            return table;
        }

        public static void SetTableCaption<TFilter>(this IWebController<IViewModel> controller, TFilter filter,
            string action, JTable table)
            where TFilter : class, IFilter
        {
            var values = controller.QueryStringToDictionary(new[] { "dt" });
            var caption = new JCaption
            {
                CurrentPage = filter.CurrentPage + 1,
                PageCount = filter.PageCount + 1,
                RowCount = filter.TotalRowCount,
            };

            caption.First = "#";
            caption.Previous = "#";
            caption.Next = "#";
            caption.Last = "#";

            caption.FirstDisabled = "disabled";
            caption.LastDisabled = "disabled";

            if (filter.CurrentPage > 0)
            {
                values["page"] = 0;
                caption.First = controller.Url.Action(action, controller.ControllerName, values);
                values["page"] = filter.CurrentPage - 1;
                caption.Previous = controller.Url.Action(action, controller.ControllerName, values);
                caption.FirstDisabled = string.Empty;
            }
            if (filter.CurrentPage
                < filter.PageCount)
            {
                values["page"] = filter.CurrentPage + 1;
                caption.Next = controller.Url.Action(action, controller.ControllerName, values);
                values["page"] = filter.PageCount;
                caption.Last = controller.Url.Action(action, controller.ControllerName, values);
                caption.LastDisabled = string.Empty;
            }

            table.Caption.Add(caption);
        }

        public static void SetHeadCell(this IWebController<IViewModel> controller, string action, ICellDescription item,
            JRow head, string controllerName)
        {
            var values = controller.QueryStringToDictionary(new[] { "dt" });

            if (values.ContainsKey("sort")
                && values["sort"].Equals(item.SortProperty))
            {
                var direction = values.ContainsKey("direction");
                if (direction)
                {
                    direction = values["direction"].ToString().IsEqual("true");
                }
                values["direction"] = !direction;
            }
            values["sort"] = item.SortProperty;

            var text = item.Localize
                           ? item.HeaderName.GetLocalized(item.HeaderNameArea ?? controllerName)
                           : item.HeaderName;
            var th = new JRowCell { Text = text };

            if (text.Length
                > item.MaxCellLength)
            {
                th.Text = text.ToLength(item.MaxCellLength);
                th.ToolTip = text;
            }

            th.Url = controller.Url.Action(action, controller.ControllerName, new RouteValueDictionary(values));

            head.Cells.Add(th);
        }

        public static JRow GetRow(this IWebController<IViewModel> controller, IItemToDisplay item, bool showRemoveAction)
        {
            var values = controller.QueryStringToDictionary(new[] { "dt" });

            var rvd = new RouteValueDictionary(values);
            rvd[Constants.Common.ID] = item.ID;

            var row = new JRow();
            row.Url = controller.Url.Action(Constants.Actions.Detail, controller.ControllerName, new { id = item.ID });

            foreach (var c in item.Items)
            {
                var cell = new JRowCell
                {
                    Text = c.Value ?? string.Empty
                };
                if (cell.Text.Length
                    > c.MaxCellLength)
                {
                    cell.ToolTip = cell.Text;
                    cell.Text = cell.Text.ToLength(c.MaxCellLength, true);
                }
                cell.Align = c.Align != Align.left
                                 ? c.Align.ToString()
                                 : null;
                row.Cells.Add(cell);
            }

            if (showRemoveAction)
            {
                var remove = new JRowCell
                {
                    Text = Constants.WebControls.Common.DoRemove2
                    .GetLocalized(controller.ControllerName).FormatWith(item.Description, item.ID),
                    ToolTip = "Remove".GetLocalized(controller.ControllerName),
                    Url = "#",
                };
                row.ID = item.ID;
                row.Remove.Add(remove);
            }
            return row;
        }
        #endregion Async Table
    }
}